﻿//#define MONAD

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Text;
using System.Threading;

using agree.composition;
using agree.schema;
using agree.mrs;

using alib;
using alib.Collections;
using alib.Debugging;
using alib.Enumerable;
using alib.String;
using alib.String.Builder;
using alib.Hashing;
using alib.Matrix;

namespace agree
{
#if MONAD
	using _functormonad_pie = m_scalar_base<IString, IIdentPair<Mrs>>;
	using _functormonad_pies = m_scalar_base<IString, IIdentPair<IItems<Mrs>>>;
	using _functormonad_pmge = m_scalar_base<IString, IIdentMasterGroup<Mrs>>;
	using _functormonad_sit = m_scalar_base<IIdentMasterGroup<Mrs>, IIdentPair<Mrs>>;
#else
	using _functormonad_pie = f_impersonate_monad<IString, IIdentPair<Mrs>>;
	using _functormonad_pies = f_impersonate_monad<IString, IIdentPair<IItems<Mrs>>>;

	using _functormonad_pmgd = f_impersonate_monad<IString, IIdentMasterGroup<IDerivation>>;
	using _functormonad_sid = f_impersonate_monad<IIdentMasterGroup<IDerivation>, IIdentPair<IDerivation>>;

	using _functormonad_pmge = f_impersonate_monad<IString, IIdentMasterGroup<Mrs>>;
	using _functormonad_sit = f_impersonate_monad<IIdentMasterGroup<Mrs>, IIdentPair<Mrs>>;
	using _functormonad_pam = f_impersonate_monad<IIdentPair<Mrs>, MrsAlignmentMatrix>;
	using _functormonad_piep = f_impersonate_monad<IIdentPair<IItems<Mrs>>, IIdentPair<IIdentList<Mrs>>>;
#endif

#if DERIVATION_PAIRING
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class DerivationPairingGrouper : _functormonad_pmgd
	{
		public DerivationPairingGrouper(IRtParent parent)
			: base(parent)
		{
		}

		public DerivationPairingGrouper(IRtParent parent, IString item)
			: base(parent, item)
		{
		}

		public IFunctor<IString, IDerivation> KeyFunctor { get; set; }
		public IFunctor<IString, IItems<IDerivation>> GroupFunctor { get; set; }

		protected override IIdentMasterGroup<IDerivation> start(IString _istring)
		{
			var mrs_set = Task.Factory.StartNew<IItems<IDerivation>>(() => GroupFunctor.Func(_istring));

			var mrs_key = KeyFunctor.Func(((IItem)_istring).Translation);

			if (mrs_key == null)
				throw new Exception();

			return new identity_master_group<IDerivation>(_istring, mrs_key, mrs_set.Result);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class SelectIsomorphicDerivation : _functormonad_sid
	{
		public SelectIsomorphicDerivation(IRtParent parent)
			: base(parent)
		{
		}
		public SelectIsomorphicDerivation(IRtParent parent, IIdentMasterGroup<IDerivation> mg)
			: base(parent, mg)
		{
		}

		protected override IIdentPair<IDerivation> start(IIdentMasterGroup<IDerivation> mg)
		{
			return new identity_pair<IDerivation>(mg, mg.FirstOrDefault(), mg.Key);
		}
	};
#endif



	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class MrsPairingGrouper : _functormonad_pmge
	{
		public MrsPairingGrouper(IRtParent parent)
			: base(parent)
		{
		}

		public MrsPairingGrouper(IRtParent parent, IString item)
			: base(parent, item)
		{
		}

		public IFunctor<IString, Mrs> KeyFunctor { get; set; }
		public IFunctor<IString, IItems<Mrs>> GroupFunctor { get; set; }

		protected override IIdentMasterGroup<Mrs> start(IString _istring)
		{
			Mrs[] mrs_set;
			Mrs mrs_key;

			var mrs_set_task = Task.Factory.StartNew<IItems<Mrs>>(() => GroupFunctor.Func(_istring));

			if ((mrs_key = KeyFunctor.Func(((IItem)_istring).Translation)) == null)
				throw new Exception();

			if ((mrs_set = mrs_set_task.Result.ToArray()).Length == 0)
				throw new Exception();

			return new identity_master_group<Mrs>(_istring, mrs_key, mrs_set);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class SelectIsomorphicMrs : _functormonad_sit
	{
		public SelectIsomorphicMrs(IRtParent parent)
			: base(parent)
		{
		}
		public SelectIsomorphicMrs(IRtParent parent, IIdentMasterGroup<Mrs> mg)
			: base(parent, mg)
		{
		}

		protected override IIdentPair<Mrs> start(IIdentMasterGroup<Mrs> mg)
		{
			return new identity_pair<Mrs>(mg, choose_mrs(mg, mg.Key, mg), mg.Key);
		}

		public static Mrs choose_mrs(IIdentity prv, Mrs en, IIdentList<Mrs> th_arr)
		{
			if (th_arr.Count == 0)
				throw new Exception();

			if (th_arr.Count == 1)
				return th_arr[0];

			foreach (var th in th_arr)
			{
				//align_mrses(prv, en, th);
			}

			var ret = th_arr[0];
			return ret;
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class MrsGroupKeyMultiplier : m_observable<IString, IIdentPair<Mrs>>
	{
		public MrsGroupKeyMultiplier(IRtParent parent, IString item)
			: base(parent, item)
		{
		}

		public IFunctor<IString, Mrs> KeyFunctor { get; set; }
		public IFunctor<IString, IItems<Mrs>> GroupFunctor { get; set; }

		protected override void start(IString _istring)
		{
			Mrs mrs_key;
			Mrs[] mrs_set;

			if ((mrs_key = KeyFunctor.Func(((IItem)_istring).Translation)) == null)
				throw new Exception();

			if ((mrs_set = GroupFunctor.Func(_istring).ToArray()).Length == 0)
				throw new Exception();

			foreach (var x in mrs_set)
				_item(new identity_pair<Mrs>(_istring, x, mrs_key));

			_remove_task();
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class PairedItemExecutor : _functormonad_pie
	{
		public PairedItemExecutor(IRtParent parent)
			: base(parent)
		{
		}
		public PairedItemExecutor(IRtParent parent, IString item)
			: base(parent, item)
		{
		}

		public IFunctor<IString, Mrs> FunctorX { get; set; }
		public IFunctor<IString, Mrs> FunctorY { get; set; }

		protected override IIdentPair<Mrs> start(IString _istring)
		{
			var t1 = Task.Factory.StartNew<Mrs>(() => FunctorX.Func(_istring));

			var r2 = FunctorY.Func(((IItem)_istring).Translation);
			if (r2 == null)
				throw new Exception();
			var r1 = t1.Result;
			if (r1 == null)
				throw new Exception();

			return new identity_pair<Mrs>(_istring, r1, r2);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class PairedItemsExecutor : _functormonad_pies
	{
		public PairedItemsExecutor(IRtParent parent)
			: base(parent)
		{
		}
		public PairedItemsExecutor(IRtParent parent, IString item)
			: base(parent, item)
		{
		}

		public IFunctor<IString, IItems<Mrs>> FunctorX { get; set; }
		public IFunctor<IString, IItems<Mrs>> FunctorY { get; set; }

		protected override IIdentPair<IItems<Mrs>> start(IString _istring)
		{
			var t1 = Task.Factory.StartNew<IItems<Mrs>>(() => FunctorX.Func(_istring));
			var r2 = FunctorY.Func(((IItem)_istring).Translation);
			return new identity_pair<IItems<Mrs>>(_istring, t1.Result, r2);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class PairedItemsProcessing : _functormonad_piep
	{
		public PairedItemsProcessing(IRtParent parent)
			: base(parent)
		{
		}
		public PairedItemsProcessing(IRtParent parent, IIdentPair<IItems<Mrs>> pr)
			: base(parent, pr)
		{
		}

		protected override IIdentPair<IIdentList<Mrs>> start(IIdentPair<IItems<Mrs>> pr)
		{
			var iax = (IdentArray<Mrs>)pr.x.ToIdentList();
			var iay = (IdentArray<Mrs>)pr.y.ToIdentList();

			if (iax.Count > 1)
				Array.Sort(iax.arr, DerivationComparer.Instance);
			if (iay.Count > 1)
				Array.Sort(iay.arr, DerivationComparer.Instance);

			var it = (Item)pr.FindTrace<Item>();

#if false
			mrs_dump("th", it.ExternalId, it.Text, iax);
			mrs_dump("en", it.ExternalId, it.Translation.Text, iay);
#endif
			return new identity_pair<IIdentList<Mrs>>(pr, iax, iay);
		}

		void mrs_dump(String lang, int ext_id, String text, IdentArray<Mrs> ia)
		{
			StringBuilder sb = new StringBuilder();

			int i = 1;
			foreach (var mrs in ia)
			{
				var deriv = mrs.FindTrace<IDerivation>();
				var score = deriv.ParseSelectionScore;
				var s_score = double.IsNaN(score) ? "" : score.ToString("N6") + " ";

				var s_id = String.Format("{0}{1}-{2}", Char.ToUpper(lang[0]), ext_id, i);
				var var_disp_bits = new BitArray(mrs.Variables.Length);

				sb.AppendLine("; ------------------");
				sb.AppendFormatLine("; {0} ({1})  {2}", s_id, ia.Count, text);
				sb.AppendFormatLine("; {0:X16} {1}{2}", deriv.DerivationHash, s_score, deriv.ItemStartSymbol.Name);
				sb.AppendLine(";");
				sb.AppendLine(mrs.ToString().Indent(";  "));
				sb.AppendLine(s_id);
				foreach (var er in mrs._ElementRoles)
				{
					var sv = er.role.Value;
					var v = sv as Variable;

					var s1 = String.Format("{0,-12} {1}", er.KeyName, sv.Name);

					if (v != null && v.Usage._Count() == 1 && !v.Any())
						s1 = s1.PadRight(40) + "; *";
					sb.AppendLine(s1);

					if (v != null && !var_disp_bits[v.num])
					{
						var_disp_bits[v.num] = true;
						foreach (var p in v)
							sb.AppendFormatLine("{0,-12} {1}", v.Name + "-" + p.Name, p.Value.Name);
					}
				}

				sb.AppendLine();

				i++;
			}

#if false
			DebugTextWriter.Instance.WriteLine(sb.ToString());
			DebugTextWriter.Instance.WriteLine("==============");
#else
			String filename = Path.Combine(@"\Programming\analytical-grammar\experiments\575-mrs-dump", lang, ext_id + ".mrs-dump");
			File.WriteAllText(filename, sb.ToString(), Encoding.UTF8);
#endif
		}
	};
}
